//
//  UINavigationController+FDFullscreenPopGesture.swift
//
//  Created by forkingdog (https://github.com/forkingdog)
//  Converted to Swift by Champion Fu
//

import ObjectiveC
import UIKit

#if canImport(FDFullscreenPopGesture)
#error("FDFullscreenPopGesture already implemented by swift")
#endif

// MARK: - Private Delegate Class

private enum AssociatedKeys {
  static var fullscreenPopGestureRecognizer: Int8 = 0
  static var popGestureRecognizerDelegate: Int8 = 0
  static var fd_willAppearInjectBlock: Int8 = 0
  static var viewControllerBasedNavigationBarAppearanceEnabled: Int8 = 0
  
  static var interactivePopDisabled: Int8 = 0
  static var prefersNavigationBarHidden: Int8 = 0
  static var ignoreView: Int8 = 0
  static var interactivePopDisabledBlock: Int8 = 0
}

@objc private class FDFullscreenPopGestureRecognizerDelegate: NSObject, UIGestureRecognizerDelegate {
  @objc weak var navigationController: UINavigationController?
    
  @objc func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
    guard let panGesture = gestureRecognizer as? UIPanGestureRecognizer,
          let nav = navigationController
    else {
      return false
    }
        
    // 导航栈中只有一个控制器时不响应
    if nav.viewControllers.count <= 1 {
      return false
    }
        
    // 获取顶层控制器
    guard let topVC = nav.viewControllers.last else { return false }
        
    // 控制器禁用交互返回时
    if topVC.fd_interactivePopDisabled {
      return false
    }
        
    // 处理禁用回调
    if let disabledBlock = topVC.fd_interactivePopDisabledBlock {
      let translation = panGesture.translation(in: panGesture.view)
      let location = panGesture.location(in: panGesture.view)
      if location.x > 40 || translation.x <= 0 {
        return false
      }
      if disabledBlock(panGesture) {
        return false
      }
    }
        
    // 忽略指定视图区域
    if let ignoreView = topVC.fd_ignoreView {
      let point = panGesture.location(in: ignoreView)
      if ignoreView.bounds.contains(point) {
        return false
      }
    }
        
    // 导航控制器正在转场时忽略
    if nav.value(forKey: "_isTransitioning") as? Bool ?? false {
      return false
    }
        
    // 检查手势方向和位置
    let translation = panGesture.translation(in: panGesture.view)
    let location = panGesture.location(in: panGesture.view)
    if location.x > 40 || translation.x <= 0 {
      return false
    }
        
    return true
  }
}

// MARK: - UIViewController Private Extension

@objc extension UIViewController {
  typealias FDWillAppearInjectBlock = (UIViewController, Bool) -> Void
    
  var fd_willAppearInjectBlock: FDWillAppearInjectBlock? {
    get {
      objc_getAssociatedObject(self, &AssociatedKeys.fd_willAppearInjectBlock) as? FDWillAppearInjectBlock
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.fd_willAppearInjectBlock, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
  }
    
  static func swizzleViewWillAppear() {
    let originalSelector = #selector(viewWillAppear(_:))
    let swizzledSelector = #selector(fd_viewWillAppear(_:))
        
    guard let originalMethod = class_getInstanceMethod(self, originalSelector),
          let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return }
        
    method_exchangeImplementations(originalMethod, swizzledMethod)
  }
    
  @objc private func fd_viewWillAppear(_ animated: Bool) {
    // 调用原方法实现
    fd_viewWillAppear(animated)
    // 执行注入的代码块
    fd_willAppearInjectBlock?(self, animated)
  }
}

// MARK: - UINavigationController Extension

@objc public extension UINavigationController {
  /// 处理全屏返回的手势识别器
  var fd_fullscreenPopGestureRecognizer: UIPanGestureRecognizer {
    if let gesture = objc_getAssociatedObject(self, &AssociatedKeys.fullscreenPopGestureRecognizer) as? UIPanGestureRecognizer {
      return gesture
    }
    let gesture = UIPanGestureRecognizer()
    gesture.maximumNumberOfTouches = 1
    objc_setAssociatedObject(self, &AssociatedKeys.fullscreenPopGestureRecognizer, gesture, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    return gesture
  }
    
  /// 手势代理
  private var fd_popGestureRecognizerDelegate: FDFullscreenPopGestureRecognizerDelegate {
    if let delegate = objc_getAssociatedObject(self, &AssociatedKeys.popGestureRecognizerDelegate) as? FDFullscreenPopGestureRecognizerDelegate {
      return delegate
    }
    let delegate = FDFullscreenPopGestureRecognizerDelegate()
    delegate.navigationController = self
    objc_setAssociatedObject(self, &AssociatedKeys.popGestureRecognizerDelegate, delegate, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    return delegate
  }
    
  /// 是否启用基于视图控制器的导航栏外观控制
  var fd_viewControllerBasedNavigationBarAppearanceEnabled: Bool {
    get {
      (objc_getAssociatedObject(self, &AssociatedKeys.viewControllerBasedNavigationBarAppearanceEnabled) as? NSNumber)?.boolValue ?? true
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.viewControllerBasedNavigationBarAppearanceEnabled, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
    
  // 方法交换初始化
  internal static func swizzlePushViewController() {
    let originalSelector = #selector(pushViewController(_:animated:))
    let swizzledSelector = #selector(fd_pushViewController(_:animated:))
        
    guard let originalMethod = class_getInstanceMethod(self, originalSelector),
          let swizzledMethod = class_getInstanceMethod(self, swizzledSelector) else { return }
        
    method_exchangeImplementations(originalMethod, swizzledMethod)
  }
    
  @objc private func fd_pushViewController(_ viewController: UIViewController, animated: Bool) {
    // 添加全屏返回手势
    if !(interactivePopGestureRecognizer?.view?.gestureRecognizers?.contains(fd_fullscreenPopGestureRecognizer) ?? false) {
      interactivePopGestureRecognizer?.view?.addGestureRecognizer(fd_fullscreenPopGestureRecognizer)
            
      // 关联系统手势的目标和动作
      if let targets = interactivePopGestureRecognizer?.value(forKey: "targets") as? [AnyObject],
         let target = targets.first?.value(forKey: "target")
      {
        let action = NSSelectorFromString("handleNavigationTransition:")
        fd_fullscreenPopGestureRecognizer.delegate = fd_popGestureRecognizerDelegate
        fd_fullscreenPopGestureRecognizer.addTarget(target, action: action)
        interactivePopGestureRecognizer?.isEnabled = false
      }
    }
        
    // 配置导航栏外观
    fd_setupViewControllerBasedNavigationBarAppearanceIfNeeded(viewController)
        
    // 调用原方法
    fd_pushViewController(viewController, animated: animated)
  }
    
  private func fd_setupViewControllerBasedNavigationBarAppearanceIfNeeded(_ appearingViewController: UIViewController) {
    guard fd_viewControllerBasedNavigationBarAppearanceEnabled else { return }
        
    weak var weakSelf = self
    let block: FDWillAppearInjectBlock = { [weak weakSelf] viewController, animated in
      guard let strongSelf = weakSelf else { return }
      strongSelf.setNavigationBarHidden(viewController.fd_prefersNavigationBarHidden, animated: animated)
    }
        
    appearingViewController.fd_willAppearInjectBlock = block
        
    if let disappearingViewController = viewControllers.last,
       disappearingViewController.fd_willAppearInjectBlock == nil
    {
      disappearingViewController.fd_willAppearInjectBlock = block
    }
  }
}

// 初始化时执行方法交换
public extension UINavigationController {
  static func initializationSwizzles() {
    UINavigationController.swizzlePushViewController()
    UIViewController.swizzleViewWillAppear()
  }
}

// MARK: - UIViewController Extension

@objc public extension UIViewController {
  /// 是否禁用交互返回
  var fd_interactivePopDisabled: Bool {
    get {
      (objc_getAssociatedObject(self, &AssociatedKeys.interactivePopDisabled) as? NSNumber)?.boolValue ?? false
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.interactivePopDisabled, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
    
  /// 导航栏是否隐藏
  var fd_prefersNavigationBarHidden: Bool {
    get {
      (objc_getAssociatedObject(self, &AssociatedKeys.prefersNavigationBarHidden) as? NSNumber)?.boolValue ?? false
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.prefersNavigationBarHidden, NSNumber(value: newValue), .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
    
  /// 需要忽略手势的视图
  var fd_ignoreView: UIView? {
    get {
      objc_getAssociatedObject(self, &AssociatedKeys.ignoreView) as? UIView
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.ignoreView, newValue, .OBJC_ASSOCIATION_RETAIN_NONATOMIC)
    }
  }
    
  /// 手势禁用的回调
  var fd_interactivePopDisabledBlock: ((UIPanGestureRecognizer) -> Bool)? {
    get {
      objc_getAssociatedObject(self, &AssociatedKeys.interactivePopDisabledBlock) as? ((UIPanGestureRecognizer) -> Bool)
    }
    set {
      objc_setAssociatedObject(self, &AssociatedKeys.interactivePopDisabledBlock, newValue, .OBJC_ASSOCIATION_COPY_NONATOMIC)
    }
  }
}
